package com.enba.boot.redis.utils;

import com.enba.boot.core.exception.BizException;
import com.enba.boot.redis.RedisOperator;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

/** 业务编码生成器 */
@Component
public class CodeGeneratorUtil {

  private final RedisTemplate<String, Object> redisTemplate;

  private final RedisOperator redisOperator;

  public CodeGeneratorUtil(
      RedisTemplate<String, Object> redisTemplate, RedisOperator redisOperator) {
    this.redisTemplate = redisTemplate;
    this.redisOperator = redisOperator;
  }

  /**
   * 生成唯一编码
   *
   * @param prefix 编码前缀（固定值）
   * @param length 编码总长度
   * @param redisKey Redis中存储序列号的Key
   * @param maxCodeGetter 从数据库中获取最大编码的函数
   * @return 生成的唯一编码
   */
  public String generateCode(
      String prefix, int length, String redisKey, Function<Void, String> maxCodeGetter) {

    // 从Redis中获取当前序列号，如果没有则从数据库中获取最大值
    int sequence = Integer.parseInt(redisOperator.incr(redisKey) + "");
    if (sequence == 1) {
      // 如果Redis中没有序列号，从数据库中获取最大值
      String lockedValue = UUID.randomUUID().toString().replaceAll("-", "");

      // 抢占分布式锁
      Boolean locked =
          redisOperator.setIfAbsent(
              "CodeGeneratorUtil:" + redisKey + ":init_lock", lockedValue, 10, TimeUnit.SECONDS);

      if (locked != null && locked) {
        try {
          // 数据库最大值
          String maxCode = maxCodeGetter.apply(null);
          sequence = maxCode == null ? 0 : Integer.parseInt(maxCode.substring(prefix.length()));

          // 设置Redis
          redisOperator.set(redisKey, sequence);

          // 自增
          sequence = Integer.parseInt(redisOperator.incr(redisKey) + "");
        } finally {
          // 防止误删（先执行的线程A删除了后执行的线程B加的锁）
          if (lockedValue.equals(
              redisOperator.get("CodeGeneratorUtil:" + redisKey + ":init_lock"))) {
            redisOperator.delete("CodeGeneratorUtil:" + redisKey + ":init_lock");
          }
        }
      } else {
        BizException.throwEx("获取序列号失败");
      }
    }

    // 生成编码，前缀 + 序列号（补零）
    return prefix + String.format("%0" + (length - prefix.length()) + "d", sequence);
  }

  public static void main(String[] args) {
    System.out.println(UUID.randomUUID().toString().replaceAll("-", ""));
  }
}
